#include <pfring.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

#include <sys/prctl.h>
#include <signal.h>

#include "common.h"
#include "acl.h"
#include "change.h"

#define MAX_PACKET_SIZE 1500
int recv_num_i = 0;

struct FILTER filter;
char *path = "/root/code/socket/acl/acl.conf";

void print_packet(const struct pfring_pkthdr *h, const u_char *p, u_int8_t dump_match)
{

    // // 按字节查看
    // u_char *tmp_p = p;
    // for (int i = 0; i < 14; i++)
    // {
    //     printf("%x ", *(tmp_p + i));

    //     if (i == 13)
    //         printf("\n");
    //     if (i > 14 && (i - 14) % 4 == 3)
    //         printf("\n");
    // }

    if (!pass_with_filter(&filter, p))
    {
        // printf("not pass\n");
        return;
    }

    struct FRAME_HEADER frame_header;
    struct IP_HEADER ip_header;
    // 将数据包保存于结构体中
    int lenght_frame_header = 14;
    memcpy(&(frame_header), p, lenght_frame_header);
    u_int8_t lenght_of_ip_header = *(p + lenght_frame_header) & 0x0f;
    lenght_of_ip_header = lenght_of_ip_header * 4;

    memcpy(&(ip_header), p + lenght_frame_header, sizeof(ip_header));
    // memcpy(&(icmp_echo), p + lenght_frame_header + lenght_of_ip_header, 24);

    // inet_ntoa()返回值为其控制的静态的固定的指针，所以每次调用 inet_ntoa()，它就将覆盖上次调用时所得的IP地址；
    // printf("%d %d", ip_header.sour_ip, ip_header.dest_ip);
    // printf("%s %s", inet_ntoa(ip_header.sour_ip), inet_ntoa(ip_header.dest_ip));

    // 提取时间信息
    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);

    struct tm *timeinfo;
    char time_now_str[128];
    timeinfo = localtime(&(tv_now.tv_sec));
    strftime(time_now_str, sizeof(time_now_str), "Time:%Y/%m/%d %H:%M:%S", timeinfo);
    sprintf(time_now_str, "%s.%ld", time_now_str, tv_now.tv_usec);

    // 提取mac信息
    char src_mac[18];
    char dst_mac[18];
    u_char *p_src_mac = p + 6;
    sprintf(src_mac, "%x:%x:%x:%x:%x:%x", *(p_src_mac), *(p_src_mac + 1),
            *(p_src_mac + 2), *(p_src_mac + 3), *(p_src_mac + 4), *(p_src_mac + 5));

    u_char *p_dst_mac = p;
    sprintf(dst_mac, "%x:%x:%x:%x:%x:%x", *(p_dst_mac), *(p_dst_mac + 1),
            *(p_dst_mac + 2), *(p_dst_mac + 3), *(p_dst_mac + 4), *(p_dst_mac + 5));

    if (frame_header.type != 0x0008)
        return;
    // 提取IP信息
    char ip_info[60];
    char src_ip[16];
    char dst_ip[16];

    strcpy(src_ip, inet_ntoa(ip_header.sour_ip));
    strcpy(dst_ip, inet_ntoa(ip_header.dest_ip));

    struct TCP_HEADER tcp_header;
    memset(&tcp_header, 0, sizeof(tcp_header));

    if (ip_header.type_of_protocol == 0x06)
    {
        u_int8_t lenght_of_tcp_header = (*(p + 14 + lenght_of_ip_header + 12) & 0xf0) >> 4;
        lenght_of_tcp_header *= 4;
        // printf("%d ", lenght_of_tcp_header);
        u_char *p_tcp_header_start = p + lenght_frame_header + lenght_of_ip_header;
        memcpy(&tcp_header, p_tcp_header_start, sizeof(tcp_header));
        int sour_port = htons(tcp_header.sour_port);
        int dest_port = htons(tcp_header.dest_port);
        sprintf(ip_info, "%s.%d->%s.%d", src_ip, sour_port, dst_ip, dest_port);
    }
    else
    {
        sprintf(ip_info, "[%s->%s]", src_ip, dst_ip);
    }

    // 提取四层信息
    char l4_info[60];
    if (tcp_header.dest_port != 0)
    {
        sprintf(l4_info, "seq:%ld , ack:%ld , wins:%ld ", htonl(tcp_header.seq), htonl(tcp_header.ack), htons(tcp_header.lenght_of_windows));
    }

    // 显示信息
    printf("[%s]", time_now_str);
    printf("[%s-->%s]", src_mac, dst_mac);
    printf("[%s]", ip_info);
    printf("[%s]", l4_info);

    printf("\n");

    recv_num_i++;
}

void sig_int(int signo)
{
    printf("\nrecived %d packets\n", recv_num_i);
    exit(0);
}

int main(int argc, char *argv[])
{

    signal(SIGINT, sig_int);
    char *device_name = "ens33";
    // u_int32_t flags = 265;

    read_line(path, &filter);

    u_int32_t flags = 0;
    pfring *ring = pfring_open(device_name, DEFAULT_SNAPLEN, flags);

    if (ring == NULL)
        return -1;

    struct pfring_pkthdr hdr;
    u_char *buffer = NULL;
    int rc = 0;

    memset(&hdr, 0, sizeof(hdr));
    ring->break_recv_loop = ring->break_recv_loop_ext = 0;

    if ((!ring) || ring->is_shutting_down || (!ring->recv) || ring->mode == send_only_mode)
        return -1;

    while (!ring->break_recv_loop_ext)
    {
        rc = ring->recv(ring, &buffer, 0, &hdr, 1);
        // pfring_recv(ring, &buffer, 0, &hdr, 1);
        print_packet(&hdr, buffer, NULL);
    }
}